package com.gf.framework.common.utils;

import com.github.benmanes.caffeine.cache.Cache;
import com.github.benmanes.caffeine.cache.Caffeine;

import java.time.Duration;
import java.time.temporal.ChronoUnit;
import java.util.Collection;
import java.util.Map;
import java.util.Set;
import java.util.function.BiConsumer;

/**
 * @author gaofei
 * @date 2022/9/2 22:47
 * CaffeineCacheMap 必须设置最大值，内部淘汰
 */
public class CaffeineCacheMap<K, V> implements Map<K, V> {
    private final Cache<K, V> cache;

    public static <K, V> Map<K, V> createTimeOut(int maxSize, long expire, ChronoUnit chronoUnit) {
        return new CaffeineCacheMap<>(Caffeine.newBuilder().softValues()
                .expireAfterWrite(Duration.of(expire, chronoUnit)).build());
    }

    public static <K, V> Map<K, V> createTimeOut(int maxSize, Duration duration) {
        return new CaffeineCacheMap<>(Caffeine.newBuilder().softValues()
                .expireAfterWrite(duration).build());
    }

    /**
     * 创建软引用map，内存回收时全部清空
     * @param maxSize 最大值
     * @param <K> key
     * @param <V> value
     * @return 创建的Map对象
     */
    public static <K, V> Map<K, V> createSoftValues(int maxSize) {
        return new CaffeineCacheMap<>(Caffeine.newBuilder().softValues().maximumSize(maxSize).build());
    }

    private CaffeineCacheMap(Cache<K, V> cache) {
        this.cache = cache;
    }

    @Override
    public int size() {
        return (int)cache.estimatedSize();
    }

    @Override
    public boolean isEmpty() {
        return 0 == size();
    }

    @Override
    public boolean containsKey(Object key) {
        return cache.getIfPresent(key) != null;
    }

    @Override
    public boolean containsValue(Object value) {
        return cache.asMap().containsValue(value);
    }

    @Override
    public V get(Object key) {
        return cache.getIfPresent(key);
    }

    @Override
    public V put(K key, V value) {
        cache.put(key, value);
        return value;
    }

    @Override
    public V remove(Object key) {
        V ret = get(key);
        cache.invalidate(key);
        return ret;
    }

    @Override
    public void putAll(Map<? extends K, ? extends V> m) {
        m.forEach((BiConsumer<K, V>) this::put);
    }

    @Override
    public void clear() {
        cache.invalidateAll();
    }

    @Override
    public Set<K> keySet() {
        return cache.asMap().keySet();
    }

    @Override
    public Collection<V> values() {
        return cache.asMap().values();
    }

    @Override
    public Set<Entry<K, V>> entrySet() {
        return cache.asMap().entrySet();
    }
}
